package com.jxtc.enterprise.tenant.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jxtc.enterprise.common.aspectj.TokenRequired;
import com.jxtc.enterprise.common.constants.DefaultValueConstants;
import com.jxtc.enterprise.common.entity.Company;
import com.jxtc.enterprise.common.entity.DeliveryLocker;
import com.jxtc.enterprise.common.mapper.CompanyMapper;
import com.jxtc.enterprise.common.mapper.DeliverLockerMapper;
import com.jxtc.enterprise.common.utils.Result;
import com.jxtc.enterprise.common.utils.ResultCode;
import com.jxtc.enterprise.common.utils.ResultGenerator;
import com.jxtc.enterprise.common.vo.PaginationResponseVO;
import com.jxtc.enterprise.tenant.vo.deliveryLocker.req.DeliveryLockerCreateReqVo;
import com.jxtc.enterprise.tenant.vo.deliveryLocker.req.DeliveryLockerUpdateReqVo;
import com.jxtc.enterprise.tenant.vo.deliveryLocker.resp.DeliveryLockerDetailRespVo;
import com.jxtc.enterprise.tenant.vo.deliveryLocker.resp.DeliveryLockerPageQueryRespVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/jxtc/enterprise/tenant/deliveryLockerManage")
@RequiredArgsConstructor
@Slf4j
@TokenRequired(role = TokenRequired.ROLE_TENANT)
public class TenantDeliveryLockerManageController {

    private final DeliverLockerMapper deliverLockerMapper;
    private final CompanyMapper companyMapper;


    /**
     * 2.10.1 分页查询外卖柜列表，支持根据外卖柜名称、外卖柜售货机编号模糊查询
     *
     * @param keyword  外卖柜名称、外卖柜售货机编号模糊查询关键字
     * @param pageNo   当前展示第几页，默认从 1 开始
     * @param pageSize 每页展示数据数量，默认为 10
     */
    @GetMapping("/queryDeliveryLockerList")
    public Result<PaginationResponseVO<DeliveryLockerPageQueryRespVo>> queryDeliveryLockerList(@RequestParam(required = false) String keyword,
                                                                                               @RequestParam(required = false, defaultValue = "1") int pageNo,
                                                                                               @RequestParam(required = false, defaultValue = "10") int pageSize,
                                                                                               HttpServletRequest request) {
        String tenantId = (String) request.getAttribute("userId");
        log.info("/queryDeliveryLockerList, keyword: {}, pageNo: {}, pageSize: {}, tenantId: {}", keyword, pageNo, pageSize, tenantId);

        // 构建分页查询条件
        Page<DeliveryLocker> page = new Page<>(pageNo, pageSize);
        LambdaQueryWrapper<DeliveryLocker> deliveryLockerQueryWrapper = new LambdaQueryWrapper<DeliveryLocker>()
                .eq(DeliveryLocker::getTenantId, tenantId)
                .eq(DeliveryLocker::getDelFlag, false)
                .orderByDesc(DeliveryLocker::getCreateTime);

        // 如果查询关键字不为空，则查询条件拼接根据外卖柜名称、简称、售货机编号的模糊查询
        if (StringUtils.hasText(keyword)) {
            deliveryLockerQueryWrapper.and(wrapper -> wrapper
                    .like(DeliveryLocker::getName, keyword)
                    .or()
                    .like(DeliveryLocker::getShortName, keyword)
                    .or()
                    .like(DeliveryLocker::getClientNo, keyword)
            );
        }

        Page<DeliveryLocker> pageResult = deliverLockerMapper.selectPage(page, deliveryLockerQueryWrapper);

        // 获取此次分页查询中外卖柜所分配的公司 ID 列表
        List<String> companyIdList = pageResult.getRecords()
                .stream()
                .map(DeliveryLocker::getCompanyId)
                // 过滤掉未绑定的公司 ID
                .filter(companyId -> !DefaultValueConstants.DELIVERY_LOCKER_NOT_BOUND_COMPANY_ID.equals(companyId))
                .collect(Collectors.toList());

        // 构建公司 ID 到公司名称的 Map
        Map<String, String> companyIdToNameMap;
        if (!CollectionUtils.isEmpty(companyIdList)) {
            LambdaQueryWrapper<Company> companyQueryWrapper = new LambdaQueryWrapper<Company>()
                    .in(Company::getId, companyIdList)
                    .select(Company::getId, Company::getName);
            List<Company> companyList = companyMapper.selectList(companyQueryWrapper);
            companyIdToNameMap = companyList.stream().collect(Collectors.toMap(Company::getId, Company::getName));
        } else {
            companyIdToNameMap = Collections.emptyMap();
        }

        // 构建返回结果
        List<DeliveryLockerPageQueryRespVo> records = pageResult.getRecords()
                .stream()
                .map(deliveryLocker -> buildDeliveryLockerPageQueryRespVo(deliveryLocker, companyIdToNameMap))
                .collect(Collectors.toList());
        PaginationResponseVO<DeliveryLockerPageQueryRespVo> vo = new PaginationResponseVO<>(pageResult.getTotal(), pageResult.getCurrent(), records);

        log.info("/queryDeliveryLockerList, response: {}", JSONObject.toJSONString(vo));
        return ResultGenerator.genSuccessResult(vo);
    }

    private DeliveryLockerPageQueryRespVo buildDeliveryLockerPageQueryRespVo(DeliveryLocker deliveryLocker, Map<String, String> companyIdToNameMap) {
        DeliveryLockerPageQueryRespVo vo = new DeliveryLockerPageQueryRespVo();
        vo.setDeliveryLockerId(deliveryLocker.getId());
        vo.setDeliveryLockerName(deliveryLocker.getName());
        vo.setDeliveryLockerShortName(deliveryLocker.getShortName());
        vo.setClientNo(deliveryLocker.getClientNo());
        vo.setAppId(deliveryLocker.getAppId());
        vo.setAppKey(deliveryLocker.getAppKey());
        vo.setCab(deliveryLocker.getCab());
        vo.setColStart(deliveryLocker.getColStart());
        vo.setColEnd(deliveryLocker.getColEnd());
        String lockerLabel = deliveryLocker.getLockerLabel();
        if (!StringUtils.hasText(lockerLabel)) {
            lockerLabel = "";
        }
        vo.setLockerLabel(lockerLabel);
        vo.setCompanyId(deliveryLocker.getCompanyId());

        if (DefaultValueConstants.DELIVERY_LOCKER_NOT_BOUND_COMPANY_ID.equals(deliveryLocker.getCompanyId())) {
            // 如果公司 ID 为 32 个 0，表示未绑定公司
            vo.setCompanyName("未绑定");
        } else {
            String companyName = companyIdToNameMap.get(deliveryLocker.getCompanyId());
            if (!StringUtils.hasText(companyName)) {
                // 如果没有找到 ID 对应的公司名称，说明出现了脏数据情况，此时记录一下方便后续排查问题
                log.info("buildDeliveryLockerPageQueryRespVo, 未找到公司名称，deliveryLockerId: {}, companyId: {}", deliveryLocker.getId(), deliveryLocker.getCompanyId());
            }
            vo.setCompanyName(companyName);
        }

        vo.setLockerLabelExample(String.format("%s%d ~ %s%d", lockerLabel, deliveryLocker.getColStart(), lockerLabel, deliveryLocker.getColEnd()));
        vo.setWaitIntervalInMs(deliveryLocker.getWaitInterval());
        return vo;
    }

    /**
     * 2.10.2 根据外卖柜 ID 查询外卖柜详情
     *
     * @param deliveryLockerId 外卖柜 ID
     */
    @GetMapping("/queryDeliveryLockerById")
    public Result<DeliveryLockerDetailRespVo> queryDeliveryLockerById(String deliveryLockerId) {
        log.info("/queryDeliveryLockerById, deliveryLockerId: {}", deliveryLockerId);
        // 参数校验
        if (!StringUtils.hasText(deliveryLockerId)) {
            throw new IllegalArgumentException("外卖柜 ID 不能为空");
        }

        DeliveryLocker deliveryLocker = deliverLockerMapper.selectById(deliveryLockerId);
        if (deliveryLocker == null) {
            log.warn("/queryDeliveryLockerById, 未找到对应的外卖柜信息，deliveryLockerId: {}", deliveryLockerId);
            throw new IllegalArgumentException("ID 为" + deliveryLockerId + " 的外卖柜不存在");
        }

        // 构建返回结果
        DeliveryLockerDetailRespVo vo = buildDeliveryLockerDetailRespVo(deliveryLocker);
        log.info("/queryDeliveryLockerById, response: {}", JSONObject.toJSONString(vo));
        return ResultGenerator.genSuccessResult(vo);
    }

    private DeliveryLockerDetailRespVo buildDeliveryLockerDetailRespVo(DeliveryLocker deliveryLocker) {
        DeliveryLockerDetailRespVo vo = new DeliveryLockerDetailRespVo();
        vo.setDeliveryLockerId(deliveryLocker.getId());
        vo.setDeliveryLockerName(deliveryLocker.getName());
        vo.setDeliveryLockerShortName(deliveryLocker.getShortName());
        vo.setClientNo(deliveryLocker.getClientNo());
        vo.setAppId(deliveryLocker.getAppId());
        vo.setAppKey(deliveryLocker.getAppKey());
        vo.setCab(deliveryLocker.getCab());
        vo.setColStart(deliveryLocker.getColStart());
        vo.setColEnd(deliveryLocker.getColEnd());
        vo.setLockerLabel(deliveryLocker.getLockerLabel());
        vo.setCompanyId(deliveryLocker.getCompanyId());
        vo.setWaitIntervalInMs(deliveryLocker.getWaitInterval());

        return vo;
    }


    /**
     * 2.10.3 新增外卖柜
     *
     * @param reqVo 新增外卖柜所需信息
     */
    @PostMapping("/addDeliveryLocker")
    public Result<Void> addDeliveryLocker(@Valid @RequestBody DeliveryLockerCreateReqVo reqVo, HttpServletRequest request) {
        String tenantId = (String) request.getAttribute("userId");
        log.info("/addDeliveryLocker, tenantId: {}, request: {}", tenantId, JSONObject.toJSONString(reqVo));

        // 去除外卖柜名称前后可能存在的空格
        String deliveryLockerName = reqVo.getDeliveryLockerName().trim();
        String lockerLabel = reqVo.getLockerLabel().trim();
        // 检查外卖柜名称和外卖柜贴标相同的记录是否已存在
        if (checkIfDeliveryLockerNameAndLockerLabelBothExists(deliveryLockerName, lockerLabel, tenantId)) {
            log.warn("/addDeliveryLocker, 外卖柜已存在, deliveryLockerName: {}, lockerLabel: {}: tenantId: {}", deliveryLockerName, lockerLabel, tenantId);
            throw new IllegalArgumentException("名称为【" + deliveryLockerName + "】且贴标为【" + lockerLabel + "】的外卖柜已存在，请更换外卖柜名称或外卖柜贴标");
        }

        // 构建外卖柜信息实体
        DeliveryLocker deliveryLocker = buildDeliveryLockerEntity(reqVo, deliveryLockerName, tenantId);

        int insertCount = deliverLockerMapper.insert(deliveryLocker);
        if (insertCount != 1) {
            // 新增失败时，记录一下日志信息（单表操作，不需要回滚）
            log.error("/addDeliveryLocker, 新增外卖柜失败, deliveryLocker: {}", JSONObject.toJSONString(deliveryLocker));
            return ResultGenerator.genFailResult(ResultCode.FAIL, "新增外卖柜失败，请稍后重试");
        }

        return ResultGenerator.genSuccessResult();
    }

    private DeliveryLocker buildDeliveryLockerEntity(DeliveryLockerCreateReqVo reqVo, String deliveryLockerName, String tenantId) {
        DeliveryLocker deliveryLocker = new DeliveryLocker();
        deliveryLocker.setName(deliveryLockerName);
        deliveryLocker.setShortName(reqVo.getDeliveryLockerShortName().trim());
        deliveryLocker.setClientNo(reqVo.getClientNo().trim());
        deliveryLocker.setAppId(reqVo.getAppId().trim());
        deliveryLocker.setAppKey(reqVo.getAppKey().trim());
        deliveryLocker.setCab(reqVo.getCab());
        deliveryLocker.setColStart(reqVo.getColStart());
        deliveryLocker.setColEnd(reqVo.getColEnd());
        deliveryLocker.setTenantId(tenantId);
        deliveryLocker.setLockerLabel(reqVo.getLockerLabel().trim());
        // 新增的外卖柜默认为未绑定状态，所以公司 ID 设置为 32 个 0
        deliveryLocker.setCompanyId(DefaultValueConstants.DELIVERY_LOCKER_NOT_BOUND_COMPANY_ID);
        deliveryLocker.setWaitInterval(reqVo.getWaitIntervalInMs());
        return deliveryLocker;
    }

    /**
     * 检查指定租户是否存在同名外卖柜，如果存在，返回 true
     */
    private boolean checkIfDeliveryLockerNameExists(String deliveryLockerName, String tenantId) {
        LambdaQueryWrapper<DeliveryLocker> queryWrapper = new LambdaQueryWrapper<DeliveryLocker>()
                .eq(DeliveryLocker::getName, deliveryLockerName)
                // 每个租户看到的外卖柜管理页面的数据是仅属于该租户的外卖柜数据
                // 如果不加租户 ID 作为筛选条件，不同租户在录入名称时可能会疑惑为什么当前外卖柜列表没有该名称，但是不能添加该名称的外卖柜
                .eq(DeliveryLocker::getTenantId, tenantId)
                .eq(DeliveryLocker::getDelFlag, false);
        return deliverLockerMapper.exists(queryWrapper);
    }

    /**
     * 检查指定租户是否存在外卖柜名称和外卖柜贴标同时相同的记录，如果存在，返回 true
     */
    private boolean checkIfDeliveryLockerNameAndLockerLabelBothExists(String deliveryLockerName, String lockerLabel, String tenantId) {
        LambdaQueryWrapper<DeliveryLocker> queryWrapper = new LambdaQueryWrapper<DeliveryLocker>()
                .eq(DeliveryLocker::getName, deliveryLockerName)
                .eq(DeliveryLocker::getLockerLabel, lockerLabel)
                // 每个租户看到的外卖柜管理页面的数据是仅属于该租户的外卖柜数据
                // 如果不加租户 ID 作为筛选条件，不同租户在录入名称时可能会疑惑为什么当前外卖柜列表没有该名称，但是不能添加该名称的外卖柜
                .eq(DeliveryLocker::getTenantId, tenantId)
                .eq(DeliveryLocker::getDelFlag, false);
        return deliverLockerMapper.exists(queryWrapper);
    }


    /**
     * 2.10.4 编辑指定外卖柜信息
     *
     * @param reqVo 编辑外卖柜所需信息
     */
    @PostMapping("/editDeliveryLocker")
    public Result<Void> editDeliveryLocker(@Valid @RequestBody DeliveryLockerUpdateReqVo reqVo, HttpServletRequest request) {
        String tenantId = (String) request.getAttribute("userId");
        log.info("/editDeliveryLocker, tenantId: {}, request: {}", tenantId, JSONObject.toJSONString(reqVo));
        // 检查指定 ID 的外卖柜是否存在
        String deliveryLockerId = reqVo.getDeliveryLockerId();
        DeliveryLocker deliveryLockerSaved = deliverLockerMapper.selectById(deliveryLockerId);
        if (deliveryLockerSaved == null) {
            log.warn("/editDeliveryLocker, 外卖柜不存在, deliveryLockerId: {}", deliveryLockerId);
            throw new IllegalArgumentException("ID 为" + deliveryLockerId + " 的外卖柜不存在");
        }

        // 判断前端传递过来的 deliveryLockerName 是否和数据库中保持的一致，不一致说明该字段做了修改
        if (StringUtils.hasText(reqVo.getDeliveryLockerName())) {
            String deliveryLockerName = reqVo.getDeliveryLockerName().trim();
            if (!deliveryLockerSaved.getName().equals(deliveryLockerName)) {
                // 如果租户修改了 deliveryLockerName，需要判断是否存在 deliveryLockerName 相同的记录
                String lockerLabel = deliveryLockerSaved.getLockerLabel();
                if (StringUtils.hasText(reqVo.getLockerLabel())) {
                    // 如果前端传递了 lockerLabel，则使用前端传递的 lockerLabel，否则使用数据库中保存的 lockerLabel
                    lockerLabel = reqVo.getLockerLabel().trim();
                }

                boolean nameAndLockerLabelBothExists = checkIfDeliveryLockerNameAndLockerLabelBothExists(deliveryLockerName, lockerLabel, tenantId);
                if (nameAndLockerLabelBothExists) {
                    // 如果修改后的外卖柜名称已经存在，则不允许修改
                    log.warn("/editDeliveryLocker, 外卖柜名称已存在, deliveryLockerName: {}, tenantId: {}", deliveryLockerName, tenantId);
                    throw new IllegalArgumentException("修改失败，名称为【" + deliveryLockerName + "】且贴标为【" + lockerLabel + "】的外卖柜已存在，请更换外卖柜名称或外卖柜贴标");
                }
            }
        }

        // 判断前端传递过来的 lockerLabel 是否和数据库中保持的一致，不一致说明该字段做了修改
        if (StringUtils.hasText(reqVo.getLockerLabel())) {
            String lockerLabel = reqVo.getLockerLabel().trim();
            if (!deliveryLockerSaved.getLockerLabel().equals(lockerLabel)) {
                // 如果租户修改了 lockerLabel，需要判断是否存在 deliveryLockerName + lockerLabel 相同的记录
                String deliveryLockerName = deliveryLockerSaved.getName();
                if (StringUtils.hasText(reqVo.getDeliveryLockerName())) {
                    // 如果前端传递了 deliveryLockerName ，则使用前端传递的 deliveryLockerName，否则使用数据库中保存的 deliveryLockerName
                    deliveryLockerName = reqVo.getDeliveryLockerName().trim();
                }

                boolean nameAndLockerLabelBothExists = checkIfDeliveryLockerNameAndLockerLabelBothExists(deliveryLockerName, lockerLabel, tenantId);
                if (nameAndLockerLabelBothExists) {
                    // 如果存在 deliveryLockerName + lockerLabel 相同的记录，则不允许修改
                    log.warn("/editDeliveryLocker, 外卖柜名称和贴标同时已存在, deliveryLockerName: {}, lockerLabel: {}, tenantId: {}", deliveryLockerName, lockerLabel, tenantId);
                    throw new IllegalArgumentException("修改失败，名称为【" + deliveryLockerName + "】且贴标为【" + lockerLabel + "】的外卖柜已存在，请更换外卖柜名称或外卖柜贴标");
                }
            }
        }

        // 检查前端传递过来的参数中哪些不为空，并去除字符串前后可能存在的空格
        DeliveryLocker deliveryLockerUpdate = checkDeliveryLockerUpdateReqVoAndTrimString(reqVo);
        int updateCount = deliverLockerMapper.updateById(deliveryLockerUpdate);
        if (updateCount != 1) {
            // 更新失败时，记录一下日志信息（单表操作，不需要回滚）
            log.error("/editDeliveryLocker, 更新外卖柜失败, deliveryLockerUpdate: {}", JSONObject.toJSONString(deliveryLockerUpdate));
            return ResultGenerator.genFailResult(ResultCode.FAIL, "更新外卖柜失败，请稍后重试");
        }

        return ResultGenerator.genSuccessResult();
    }

    private DeliveryLocker checkDeliveryLockerUpdateReqVoAndTrimString(DeliveryLockerUpdateReqVo reqVo) {
        DeliveryLocker deliveryLocker = new DeliveryLocker();
        deliveryLocker.setId(reqVo.getDeliveryLockerId());

        if (StringUtils.hasText(reqVo.getDeliveryLockerName())) {
            deliveryLocker.setName(reqVo.getDeliveryLockerName().trim());
        }

        if (StringUtils.hasText(reqVo.getDeliveryLockerShortName())) {
            deliveryLocker.setShortName(reqVo.getDeliveryLockerShortName().trim());
        }

        if (StringUtils.hasText(reqVo.getClientNo())) {
            deliveryLocker.setClientNo(reqVo.getClientNo().trim());
        }

        if (StringUtils.hasText(reqVo.getAppId())) {
            deliveryLocker.setAppId(reqVo.getAppId().trim());
        }

        if (StringUtils.hasText(reqVo.getAppKey())) {
            deliveryLocker.setAppKey(reqVo.getAppKey().trim());
        }

        if (reqVo.getCab() != null) {
            deliveryLocker.setCab(reqVo.getCab());
        }

        if (reqVo.getColStart() != null) {
            deliveryLocker.setColStart(reqVo.getColStart());
        }

        if (reqVo.getColEnd() != null) {
            deliveryLocker.setColEnd(reqVo.getColEnd());
        }

        if (StringUtils.hasText(reqVo.getLockerLabel())) {
            deliveryLocker.setLockerLabel(reqVo.getLockerLabel().trim());
        }

        if (reqVo.getWaitIntervalInMs() != null) {
            deliveryLocker.setWaitInterval(reqVo.getWaitIntervalInMs());
        }

        return deliveryLocker;
    }


    /**
     * 2.10.5 删除指定外卖柜
     *
     * @param deliveryLockerId 外卖柜 ID
     */
    @GetMapping("/deleteDeliveryLockerById")
    public Result<Void> deleteDeliveryLockerById(@RequestParam String deliveryLockerId) {
        log.info("/deleteDeliveryLockerById, deliveryLockerId: {}", deliveryLockerId);
        DeliveryLocker deliveryLockerSaved = deliverLockerMapper.selectById(deliveryLockerId);
        if (deliveryLockerSaved == null) {
            log.warn("/deleteDeliveryLockerById, 外卖柜不存在, deliveryLockerId: {}", deliveryLockerId);
            throw new IllegalArgumentException("删除失败，ID 为" + deliveryLockerId + " 的外卖柜不存在");
        }

        DeliveryLocker deliveryLockerDelete = new DeliveryLocker();
        deliveryLockerDelete.setId(deliveryLockerId);
        deliveryLockerDelete.setDelFlag(true);
        int deleteCount = deliverLockerMapper.updateById(deliveryLockerDelete);
        if (deleteCount != 1) {
            // 删除失败时，记录一下日志信息（单表操作，不需要回滚）
            log.error("/deleteDeliveryLockerById, 删除外卖柜失败, deliveryLockerDelete: {}", JSONObject.toJSONString(deliveryLockerDelete));
            return ResultGenerator.genFailResult(ResultCode.FAIL, "删除失败，请稍后重试");
        }
        return ResultGenerator.genSuccessResult();
    }
}
